/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-13
 * V4.0
 */
package com.jphenix.webserver.instancea;

import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;

import com.jphenix.servlet.filter.BaseFilter;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.lang.SInteger;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.exceptions.EmptyException;
import com.jphenix.standard.servlet.api.IFilterConfig;
import com.jphenix.standard.servlet.api.IHttpSession;
import com.jphenix.standard.servlet.api.IServlet;
import com.jphenix.ver.PhenixVer;
import com.jphenix.webserver.api.FilterConfigImpl;
import com.jphenix.webserver.api.HttpSessionContextImpl;
import com.jphenix.webserver.api.ServletContextImpl;
import com.jphenix.webserver.api.SessionImpl;

/*
 * 嵌入式Web服务器
 * 主要用来开发和调试网站程序
 * 或用于小型网站或应急使用
 */

/**
 * 服务入口类
 * 
 * 
 * 2018-05-22 改为，如果不设置绑定网卡IP地址，则允许响应所有本机网卡地址
 * 2018-11-02 完善了输入错的URL时，后台留下的日志，便于找到问题
 * 2019-01-17 在终止服务程序钩子中增加了提示日志
 * 2019-09-17 整理了代码格式
 * 2021-03-17 适配tomcat9中的 servlet-api.jar
 * 2022-09-04 重构了内置Servlet容器，不依赖外部ServletApi包
 * 2023-12-20 增加了单独运行SSL模式
 * 2024-06-05 去掉了文件处理类，没有必要使用
 * 2024-07-23 新增连接超时参数
 * 
 * com.jphenix.webserver.instancea.Serve
 * @author 刘虻
 * 2006-9-15上午12:08:44
 */
@ClassInfo({"2024-07-23 17:20","服务入口类"})
public class Serve implements Serializable {
	
	/**
	 * 版本标识
	 */
	private static final long serialVersionUID = -8257580690642041677L;
 
	private transient boolean            running = true;   //服务是否运行
	private transient KeepAliveCleaner   keepAliveCleaner; //连接清除类
	private transient ThreadGroup        serverThreads;    //服务处理线程
	private transient ThreadManager      threadManager;    //线程池
	private transient ServerSocket       serverSocket;     //服务器端口
	private transient ServerSocket       sslServerSocket;  //SSL服务器端口
	private transient Thread             ssclThread;       //会话处理线程
	
	private String   hostName;                  // 服务器名
	private String   bindAddress        = null; // 绑定地址
	private String   keepAliveHdrParams = null; // http头keepAlive参数
	private String   serverName         = null; // 服务器名
	private String   sessionCookieName  = null; // 会话主键名
	private String[] welcomePages       = null; // 默认显示文件数组
	
	private int   timeoutKeepAlive;      // 浏览器与服务器连接超时时间
	private int   maxAliveConnUse;       // 浏览器与服务器连接数
	private int   uniqer;                // 会话主键序列(防止会话主键重复
	
	private long startTime          = System.currentTimeMillis(); //启动开始时间
	
	private HttpSessionContextImpl          sessions;                   // 会话上下文
	private ServletContextImpl              servletContext      = null; // servlet上下文
	private Thread                          sdHook              = null; // 程序关闭钩子
	private ServeParameter                  serveParameter      = null; // 服务参数处理类
	private Log                             log                 = null; // 日志处理类
	private ArrayList<IServlet>             noUseServletList    = null; // 获取只加载不使用的Servlet
	
	protected BaseFilter                    filter              = null; // 过滤器
	
	//内部web服务器：比如生产环境运行着tomcat，但是像脚本操作，文件操作等高风险
	//动作，不能开放到外网访问，就需要项目另监听一个端口，专门用来内部操作。
	protected boolean isNativeServ = false; //是否为内部Web服务器
	private   boolean keepAlive;            //浏览器与服务器是否保持连接
	
	private final String ARG_OUT_SERVLOG                 = "out-serv-log";                // 是否输出服务器普通日志
	private final String DEF_LOGENCODING                 = "UTF-8";                       // 默认日志编码
	private final String ARG_MAX_EXECUTE_THREAD_COUNT    = "max-execute_thread_count";    // 服务器最大线程数
	private final String ARG_SESSION_COOKIE_NAME         = "SessionCookieName";           // 会话主键名
	private final String DEF_SESSION_COOKIE_NAME         = "JSESSIONID";                  // 默认会话主键名
	private final String ARG_KEEPALIVE                   = "keep-alive";                  // 是否浏览器与服务器建立持久连接
	private final String ARG_KEEPALIVE_TIMEOUT           = "timeout-keep-alive";          // 浏览器与服务器保持连接超时时间
	private final String ARG_MAX_CONN_USE                = "max-alive-conn-use";          // 服务器与浏览器最大连接数
	private final String ARG_BINDADDRESS                 = "bind-address";                // 服务器绑定地址
	private final String ART_ENABLED_SSL                 = "enabled-ssl";                 // 是否启用ssl连接
	private final String ART_ONLY_SSL                    = "only-ssl";                    // 是否只开放ssl链接
	private final String ARG_AFTER_START_EXECUTE_COMMAND = "after-start-execute-command"; // 网站启动后执行的命令行
	private final String ARG_BACKLOG                     = "backlog";                     // 默认并发连接数参数
	private final String ARG_PORT                        = "port";                        // 监听端口
	private final int    DEF_PORT                        = 80;                            // 默认端口
	private final int    DEF_MAX_CONN_USE                = 100;                           // 默认连接数
	
	/**
	 * 构造参数
	 * @author 刘虻
	 * 2007-9-29下午04:54:24
	 */
	public Serve() {
		super();
	}
	
	/**
	 * 执行监听线程
	 * @author 刘虻
	 * 2008-7-8下午04:06:28
	 */
	protected class ListenAcceptThread extends Thread {
		
		protected ServerSocket sSocket     = null; // 服务器Socket
		protected Serve        serve       = null; // 服务类
		protected int          returnValue = 0;    // 返回值
		
		/**
		 * 构造函数
		 * 2008-7-8下午04:06:50
		 */
		public ListenAcceptThread(ServerSocket sSocket,Serve serve,boolean isSsl) {
			super("Serv-ListenAcceptThread-"+(isSsl?"https":"http"));
			this.sSocket = sSocket;
			this.serve = serve;
		}
		
		/**
		 * 获取返回值
		 * @author 刘虻
		 * 2008-7-8下午04:09:48
		 * @return 返回值
		 */
		public int getReturnValue() {
			return returnValue;
		}
		
		/**
		 * 覆盖方法
		 * @author 刘虻
		 * 2008-7-8下午04:06:59
		 */
		@Override
        public void run() {
			try {
				while (running) {
					try {
						//获取浏览器与服务器连接Socket
						Socket socket = sSocket.accept();
						
						//在ServeConnection的构造函数中启动了线程管理
						new ServeConnection(socket, serve, filter);
					} catch (IOException e) {
						log.log("Accept: " + e);
					} catch (SecurityException se) {
						log.log("Illegal access: " + se);
					}
				}
			} catch (Throwable t) {
				log.log("Unhandled exception: " + t + ", server is terminating.", t);
				returnValue = -1;
				return;
			} finally {
				try {
					if (sSocket != null) {
                        sSocket.close();
                    }
				} catch (IOException e) {
				}
			}
		}
	}
	
	/**
	 * 启动入口
	 * 
     * 当第一个参数为 1 时，为直传参数模式
     * 在该模式下，第二个参数为监听端口
     * 第三个传入参数为虚拟路径
     * 直传参数模式只支持者两个参数
     * 
	 * @author 刘虻
	 * 2007-9-29下午04:38:45
	 * @param args 导入参数
	 */
	public static void main(String[] args) {
		//构造服务类实例
		Serve serve = new Serve();
		//启动服务
		serve.startup(args);
	}

	/**
	 * 获取服务参数处理类实例
	 * @author 刘虻
	 * 2007-9-29下午04:58:47
	 * @return 服务参数处理类实例
	 */
	public ServeParameter getServeParameter() {
		if (serveParameter==null) {
			serveParameter = new ServeParameter();
		}
		return serveParameter;
	}
	
	/**
	 * 获取系统日志流
	 * @author 刘虻
	 * 2006-9-15上午11:22:18
	 * @param argumentMap 导入参数容器 from=givenpath;dir=realpath
	 * @return 系统日志流
	 */
	public void initLog() {
		//构建日志类实例
		log = new Log();
		//是否输出普通日志
		log.setOutLog(getServeParameter().getArgumentBoolean(ARG_OUT_SERVLOG));
		//设置日志默认编码格式
		log.setLogEncoding(System.getProperty(DEF_LOGENCODING));
		//设置工作路径
		log.setWorkPath(getServeParameter().getServletBasePath()+"/logs");
		//设置输出日志流
		log.setLogStream(System.out);
	}
	
	/**
	 * 执行初始化
	 * @author 刘虻
	 * 2007-9-30下午02:43:33
	 */
	protected void initialize(String[] args) throws Exception {

		//初始化导入参数
		getServeParameter().initProp(args); 
		
		//初始化日志类
		initLog();
		
		if (!getServeParameter().getArgumentBoolean("nover")) {
			PhenixVer.printVERShowInfo(); //显示版本信息
		}
		
		System.out.println(">> [" + new Date() + "] Load Server Config File:["+getServeParameter().getConfigFilePath()+"]");
		System.out.println(">>Set Base Web Path:["+getServeParameter().getWebBasePath()+"]");
		System.out.println(">>Set Servlet Base Path:["+getServeParameter().getServletBasePath()+"]");
		System.out.println(">>Set Servlet Context Path:["+getServeParameter().getArgumentMap().get("context-path")+"]");

		//构建线程池组
		serverThreads = new ThreadGroup("Phenix WebServer threads");
		
		//构建线程管理类
		threadManager = new ThreadManager();
		//最大处理线程数
		int maxThreadCount = getServeParameter().getArgumentInt(ARG_MAX_EXECUTE_THREAD_COUNT);
		if (maxThreadCount>0) {
			threadManager.setMaxThreadCount(maxThreadCount);
		}
		
		threadManager.start(); //启动线程管理
		
		//会话主键名
		sessionCookieName = getServeParameter().getArgumentString(ARG_SESSION_COOKIE_NAME);
		if (sessionCookieName.length()<1) {
			sessionCookieName = DEF_SESSION_COOKIE_NAME;
		}
		keepAlive = getServeParameter().getArgumentBoolean(ARG_KEEPALIVE);
		//浏览器与服务器通信保持时间(秒)
		int timeoutKeepAliveSec = getServeParameter().getArgumentInt(ARG_KEEPALIVE_TIMEOUT);
		if (timeoutKeepAliveSec<1) {
			timeoutKeepAliveSec = 30;
		}
		timeoutKeepAlive = timeoutKeepAliveSec * 1000;
		
		//并发线程数
		maxAliveConnUse = getServeParameter().getArgumentInt(ARG_MAX_CONN_USE);
		if (maxAliveConnUse<1) {
			maxAliveConnUse = DEF_MAX_CONN_USE;
		}
		//处理http头keepAlive参数
		keepAliveHdrParams = "timeout="+timeoutKeepAliveSec+", max=" + maxAliveConnUse;

		//添加关闭钩子
		Runtime.getRuntime().addShutdownHook(
				//构建新的关闭钩子
				sdHook = new Thread(new Runnable() {
					
				/**
				 * 覆盖方法
				 * @author 刘虻
				 * 2007-9-30下午03:27:03
				 */
				@Override
                synchronized public void run() {
					System.err.printf("\n\n\n*******************The Web Serv Has Stoped***********************\n\n\n");
					if(filter!=null) {
						filter.destroy();
					}
				}
					
			},"ShutDownHook"));
	}

	/**
	 * 执行启动服务
	 * 
     * 当第一个参数为 1 时，为直传参数模式
     * 在该模式下，第二个参数为监听端口
     * 第三个传入参数为虚拟路径
     * 直传参数模式只支持者两个参数
	 * 
	 * @author 刘虻
	 * 2007-9-29下午04:39:42
	 * @param args 导入参数
	 */
	public void startup(String[] args) {
		try {
			initialize(args); //执行初始化
		}catch(EmptyException e0) {
			return;
		}catch(Exception e) {
			e.printStackTrace();
			return;
		}
		if(filter==null) {
			// 构建过滤器配置文件实例
			IFilterConfig fConf = new FilterConfigImpl(this);
			filter = new BaseFilter();
			try {
				filter.init(fConf);
			}catch(Exception e) {
				e.printStackTrace();
				return;
			}
		}
		int code = doServe();
		Runtime.getRuntime().removeShutdownHook(sdHook);
		killAliveThreads();
		System.out.println("\n\n16-----------------------------------------------------------------------\nThe Application Has Bean Stoped\n--------------------------------------------------------------------------------------\n");
		System.exit(code);
	}
	
	/**
	 * 获取浏览器与服务器是否保持连接
	 * @author 刘虻
	 * 2008-6-28下午06:52:12
	 * @return 浏览器与服务器是否保持连接
	 */
	public boolean isKeepAlive() {
		return keepAlive;
	}

	/**
	 * 获取浏览器与服务器连接超时时间
	 * @author 刘虻
	 * 2008-6-28下午06:52:29
	 * @return 浏览器与服务器连接超时时间
	 */
	public int getKeepAliveDuration() {
		return timeoutKeepAlive;
	}

	/**
	 * 获取http头keepAlive参数
	 * @author 刘虻
	 * 2007-9-30下午03:34:15
	 * @return http头keepAlive参数
	 */
	public String getKeepAliveParamStr() {
		return keepAliveHdrParams;
	}

	/**
	 * 获取并发连接数
	 * @author 刘虻
	 * 2008-6-28下午06:51:02
	 * @return 并发连接数
	 */
	public int getMaxTimesConnectionUse() {
		return maxAliveConnUse;
	}
	
	/**
	 * 获取绑定地址
	 * @author 刘虻
	 * 2008-6-28下午05:00:36
	 * @return 绑定地址
	 */
	public String getBindAddress() {
		if (bindAddress==null) {
			bindAddress = 
				getServeParameter().getArgumentString(ARG_BINDADDRESS);
		}
		return bindAddress;
	}
	
	/**
	 * 启动服务器
	 * @author 刘虻
	 * 2008-6-28下午04:59:29
	 * @return 启动返回值
	 * @deprecated
	 */
	public int doServe() {
		
		//#region 处理连接超时时间
		Integer timeOut = null;
		String  timeOutStr = getServeParameter().getExtPara().value("ConnTimeOut");
		if(timeOutStr.length()>0) {
			timeOut = SInteger.valueOf(timeOutStr)*1000;
		}
		//#endregion
		
		boolean enabledHttp = false; // 是否允许http监听
		if(!getServeParameter().getArgumentBoolean(ART_ONLY_SSL)) {
			try {
				//获取服务器Socket
				serverSocket = createServerSocket();
				if (getBindAddress().length()<1) {
					hostName = serverSocket.getInetAddress().getHostName();
				}else {
					hostName = InetAddress.getLocalHost().getHostName();
				}
				if(timeOut!=null) {
					serverSocket.setSoTimeout(timeOut);
				}
				enabledHttp = true;
			} catch (IOException e) {
				log.log("Server socket: " + e);
				return 1;
			}
		}
		
		//是否启用SSL
		boolean enabledSsl = 
			getServeParameter().getArgumentBoolean(ART_ENABLED_SSL);
		if (enabledSsl) {
			try {
				//建立SSL连接
				sslServerSocket = createSSLServerSocket();
				
				if(timeOut!=null) {
					sslServerSocket.setSoTimeout(timeOut);
				}
			}catch(Exception e) {
				e.printStackTrace();
				log.log("SSL Server socket: " + e);
				return 1;
			}
		}
		//判断会话是否超时
		if (getServeParameter().getSessionTimeOut() > 0) {
			ssclThread = new Thread(serverThreads, new Runnable() {
				@Override
                public void run() {
					while (running) {
						try {
							Thread.sleep(getServeParameter().getSessionTimeOut() * 60 * 1000);
						} catch (InterruptedException ie) {
							if (running == false) {
                                break;
                            }
						}
						//获取会话主键迭代器
						Enumeration<String> e = sessions.keys();
						while (e.hasMoreElements()) {
							Object sid = e.nextElement();
							if (sid != null) {
								//获取会话对象
								SessionImpl as = (SessionImpl)sessions.get(sid);
								if (as!= null 
										&& (as.checkExpired() || !as.isValid())) {
									removeSession(as); //移除会话
								}
							}
						}
					}
				}
			}, "Session cleaner");
			ssclThread.setPriority(Thread.MIN_PRIORITY);
			ssclThread.start();
		}
		if (isKeepAlive()) {
			keepAliveCleaner = new KeepAliveCleaner();
			keepAliveCleaner.setTimeoutKeepAlive(timeoutKeepAlive);
			keepAliveCleaner.start();
		}
		if (sessions == null) {
			sessions = new HttpSessionContextImpl();
		}
		ListenAcceptThread httpThread = null; // 监听http线程
		ListenAcceptThread sslThread  = null; // 监听https线程
		if(isNativeServ) {
			if(enabledHttp) {
				System.out.println("\n\n********************************************************************************************************\nStart Native Web Server http:[" + serverSocket 
						+ "]is listening.\n********************************************************************************************************\n\n");
				httpThread =  (new ListenAcceptThread(serverSocket,this,false));
			}
			if (enabledSsl) {
				System.out.println("\n\n********************************************************************************************************\nStart SSL Native Web Server https:[" 
						+ sslServerSocket + "]is listening.\n********************************************************************************************************\n\n");
				sslThread = (new ListenAcceptThread(sslServerSocket,this,true));
			}
		}else {
			if(enabledHttp) {
			      System.out.println("\n\n(^_^)∠※ [" + new Date() + "] Start http " + hostName + " - " + serverSocket 
			                + " is listening. start used:"+SDate.runTimeInfo(System.currentTimeMillis()-startTime)+"\n\n");
			      httpThread =  (new ListenAcceptThread(serverSocket,this,false));
			      httpThread.start();
			}
			if (enabledSsl) {
				System.out.println("\n\n(^_^)∠※ [" + new Date() + "] Start SSL https " + hostName + " - "
			   + sslServerSocket + " is listening. start used:"+SDate.runTimeInfo(System.currentTimeMillis()-startTime)+"\n\n");
				sslThread = (new ListenAcceptThread(sslServerSocket,this,true));
				sslThread.start();
			}
		}
		
		//在启动服务后需要执行的命令行
		String executeCommand = 
				getServeParameter()
					.getArgumentString(ARG_AFTER_START_EXECUTE_COMMAND);
		if(executeCommand.length()>0) {
			try {
				//在启动后需要执行的命令
				Runtime.getRuntime().exec(executeCommand);
			}catch(Exception e) {
				e.printStackTrace();
			}
		}
		try {
			if(httpThread!=null) {
				httpThread.join();
			}
			if(sslThread!=null) {
				sslThread.join();
			}
		}catch(Exception e) {
			e.printStackTrace();
			return -1;
		}
		return 1;
	}

	/**
	 * 停止服务
	 * @author 刘虻
	 * 2008-6-28下午06:44:53
	 */
	protected void notifyStop() {
		running = false;
		if(serverSocket!=null) {
			try {
				serverSocket.close();
			}catch(Exception e) {}
		}
		serverSocket = null;
		
		if(sslServerSocket!=null) {
			try {
				sslServerSocket.close();
			}catch(Exception e) {}
		}
		sslServerSocket = null;
	}

	/**
	 * 终止所有线程
	 * @author 刘虻
	 * @deprecated
	 * 2008-6-28下午06:45:16
	 */
    protected void killAliveThreads() {
		serverThreads.interrupt();
		ThreadGroup tg = Thread.currentThread().getThreadGroup();
		while (tg.getParent() != null) {
            tg = tg.getParent();
        }
		int ac = tg.activeCount() + tg.activeGroupCount() + 10;

		Thread[] ts = new Thread[ac];
		ac = tg.enumerate(ts, true);
		if (ac == ts.length) {
			log.log("Destroy:interruptRunningProcesses: Not all threads will be stopped.");
        }
		for (int i = 0; i < ac; i++) {
            if (ts[i].isDaemon() == false) {
                String tn = ts[i].getName();
                if (ts[i] == Thread.currentThread() || "Stop Monitor".equals(tn) || "ShutDownHook".equals(tn)
                        || "DestroyJavaVM".equals(tn) || (tn != null && tn.startsWith("AWT-"))) {
                    continue;
                }
                ts[i].interrupt();
                Thread.yield();
                if (ts[i].isAlive()) {
                    try {
                        ts[i].stop();
                    } catch (Throwable t) {
                        if (t instanceof ThreadDeath) {
                            throw (Error) t;
                        }
                    }
                }
            }
        }
	}

	/**
	 * 停止服务
	 * @author 刘虻
	 * 2008-6-28下午05:10:32
	 * @throws IOException 执行发生异常
	 */
	public void stop() throws IOException {
		notifyStop();
	}
	
	
	/**
	 * 建立SSL安全连接
	 * @author 刘虻
	 * 2008-7-7下午06:44:23
	 * @return SSL安全连接
	 * @throws IOException 执行发生异常
	 */
	protected ServerSocket createSSLServerSocket() throws Exception {
		return (new SSLServerSocketFactory()).createSocket(getServeParameter());
	}
	

	/**
	 * 建立服务器连接
	 * @author 刘虻
	 * 2008-6-28下午05:42:49
	 * @return 服务器连接实例
	 * @throws IOException 执行发生异常
	 */
	protected ServerSocket createServerSocket() throws IOException {
		int backlog = getServeParameter().getArgumentInt(ARG_BACKLOG);
		if (backlog<0) {
			backlog = 0;
		}
		//获取服务器监听端口
		int port = getServeParameter().getArgumentInt(ARG_PORT);
		if (port<1) {
			port = DEF_PORT;
		}
		InetAddress ia = null; //绑定地址类
		if (getBindAddress().length()>0) {
			try {
				ia = InetAddress.getByName(getBindAddress());
			} catch (Exception e) {}
		}
		try {
			if(ia!=null) {
				return new ServerSocket(port,backlog, ia);
			}else {
				return new ServerSocket(port);
			}
		}catch(IOException e2) {
			//构建错误信息
			StringBuffer errorMsg = new StringBuffer();
			
			errorMsg
				.append("\n\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n")
				.append("Create Socket Port:[")
				.append(port)
				.append("] Backlog:[")
				.append(backlog)
				.append("] BindAddress:[")
				.append(getBindAddress())
				.append("] Exception:[")
				.append(e2)
				.append("]\n")
				.append("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n\n\n");
			
			System.err.print(errorMsg);
			log.log(errorMsg.toString(),e2);
			throw e2;
		}
	}

	/**
	 * 获取会话
	 * @author 刘虻
	 * 2008-6-28下午07:10:34
	 * @param id 会话主键
	 * @return 当前会话
	 */
	public IHttpSession getSession(String id) {
		return sessions.getSession(id);
	}
	
	/**
	 * 获取会话上下文
	 * @return 会话上下文
	 * 2024年7月15日
	 * @author MBG
	 */
	public HttpSessionContextImpl getSessionContextImpl() {
		return sessions;
	}
	
	/**
	 * 建立会话
	 * @author 刘虻
	 * 2008-6-28下午07:09:13
	 * @return 新的会话
	 */
	public IHttpSession createSession(ServeConnection servConn,BaseFilter baseFilter) {
		//构造新的会话
		IHttpSession result = 
			new SessionImpl(
					  generateSessionId()
					, Math.abs(getServeParameter().getSessionTimeOut()) * 60
					, getServletContext()
					, sessions
					, servConn
					, baseFilter);
		sessions.put(result.getId(),result);
		return result;
	}

	/**
	 * 移除会话
	 * @author 刘虻
	 * 2008-6-28下午07:10:13
	 * @param id 会话主键
	 */
	public void removeSession(String id) {
		removeSession((IHttpSession)sessions.get(id));
	}
	
	
	/**
	 * 移除会话
	 * @author 刘虻
	 * 2008-6-28下午07:15:54
	 * @param session 会话
	 */
	protected void removeSession(IHttpSession session) {
		//从容器中除去指定会话
		IHttpSession as = (IHttpSession) sessions.remove(session.getId());
		if (as==null 
				|| as.getLastAccessedTime()+as.getMaxInactiveInterval()*1000<System.currentTimeMillis()) {
			try {
				as.invalidate();
			} catch (IllegalStateException ise) {}
		}
	}

	/**
	 * 获取新的会话主键
	 * @author 刘虻
	 * 2008-6-28下午06:41:14
	 * @return 新的会话主键
	 */
	protected synchronized String generateSessionId() {
		if (uniqer>99999999) {
			uniqer = 0;
		}
		return (new StringBuffer(getServerName()))
					.append("-")
					.append(System.currentTimeMillis())
					.append(uniqer++)
					.append("-")
					.append(Math.round(Math.random() * 1000))
					.toString();
	}
	
	/**
	 * 获取Servlet上下文
	 * @return Servlet上下文
	 * 2022年9月3日
	 * @author MBG
	 */
	public ServletContextImpl getServletContext() {
		if(servletContext==null) {
			servletContext = new ServletContextImpl(this);
		}
		return servletContext;
	}

	/**
	 * 输出日志
	 * @param message 日志内容
	 */
    public void log(String message) {
		log.log(message);;
	}
    
	/**
	 * 输出日志
	 * @param message   日志内容
	 * @param throwable 异常堆栈
	 */
    public void log(String message,Throwable throwable) {
    	log.log(message,throwable);
    }
	
	/**
	 * 设置服务器名
	 * @author 刘虻
	 * 2008-6-28下午07:06:11
	 * @param serverName 服务器名
	 */
	public void setServerName(String serverName) {
		this.serverName = serverName;
	}
	
	/**
	 * 获取服务器名
	 * @author 刘虻
	 * 2008-6-28下午07:06:20
	 * @return 服务器名
	 */
	public String getServerName() {
		if (serverName==null) {
			serverName = "PX"+System.currentTimeMillis();
		}
		return serverName;
	}
	
	/**
	 * 获取会话主键名
	 * @author 刘虻
	 * 2008-6-28下午07:25:33
	 * @return 会话主键名
	 */
	public String getSessionCookieName() {
		return sessionCookieName;
	}
	
	/**
	 * 获取连接移除管理类
	 * @author 刘虻
	 * 2008-10-30下午03:52:10
	 * @return 连接移除管理类
	 */
	public KeepAliveCleaner getKeepAliveCleaner() {
		return keepAliveCleaner;
	}
	
	
	/**
	 * 获取线程管理器
	 * @author 刘虻
	 * 2008-10-30下午03:53:17
	 * @return 线程管理器
	 */
	public ThreadManager getThreadManager() {
		return threadManager;
	}
	
	/**
	 * 获取获取只加载不使用的Servlet
	 * @author 刘虻
	 * 2008-7-28下午12:37:26
	 * @return 获取只加载不使用的Servlet
	 */
	protected ArrayList<IServlet> getNoUseServletList() {
		if (noUseServletList==null) {
			noUseServletList = new ArrayList<IServlet>();
		}
		return noUseServletList;
	}
	
	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-10-30下午03:46:42
	 */
	public String getHostName() {
		return hostName;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-10-30下午06:57:02
	 */
	public boolean isRunning() {
		return running;
	}
	
	
	/**
	 * 获取默认显示文件字符串数组
	 * 
	 * @author 刘虻 2009-7-22下午03:27:40
	 * @return 默认显示文件字符串数组
	 */
	public String[] getWelcomeFilePages() {
		if (welcomePages == null) {
			if (serveParameter != null) {
				welcomePages = new String[serveParameter.getWelcomeFileList().size()];
				serveParameter.getWelcomeFileList().toArray(welcomePages);
			}
			if (welcomePages == null || welcomePages.length < 1) {
				welcomePages = new String[0];
			}
		}
		return welcomePages;
	}
	
	/**
	 * 设置根过滤器
	 * @param baseFilter 根过滤器
	 * 2022年9月3日
	 * @author MBG
	 */
	public void setBaseFilter(BaseFilter baseFilter) {
		this.filter = baseFilter;
	}
}